// 模拟车站卖票的程序
#include <iostream>
#include <string>
#include<thread>
#include<list>
#include<mutex>

using namespace std;
// 一共有tickCount张票
int tickCount = 10;
// 定义全局互斥锁
mutex mtx;

void sellTicket(int index)
{
    // mtx.lock(); //1、这样就只会存在一个窗口在卖票，因为while循环只支持一个线程访问
    while(tickCount>0)
    {   
        /*
        直接使用mutex.lock()和mutex.unlock()将会出现一个问题，
        当函数还未unlock时就因为程序中间return或者error结束后，
        导致mutex资源无法释放的问题。
        */
        mtx.lock(); //2、仅在临界区代码段 -> 原子操作 -> 线程间互斥操作 -> mutex
        // 在锁里面加判断是防止当一个进程1面临tickCount=1时，还为-1成功，
        // 另一个线程2进入while循环，只是在mtx.lock阻塞了，等进程1-1成功后，
        // 其实进程2获取到的tickCount已经由1->0，但是还是会进行tickCount--
        // 导致了最终卖出-1张票
        if(tickCount>0)
        {
            cout << "窗口:" << index << "卖出第" << tickCount << "张票。" << endl;
            tickCount--;
        }
        mtx.unlock(); //2、
        this_thread::sleep_for(chrono::milliseconds(2));
    }
    // mtx.unlock(); // 1、
}

void sellTicket2(int index)
{
    while (tickCount > 0)
    {
        
        // mtx.lock(); 
        {
            // lock_guard函数删除了拷贝构造函数和操作符=重载，类似于scoped_ptr，但保留了构造函数
            lock_guard<mutex> lock(mtx); //在这个局部作用域中，程序结束后自动析构，如果中间return了也会析构
            if (tickCount > 0)
            {
                cout << "窗口:" << index << "卖出第" << tickCount << "张票。" << endl;
                tickCount--;
        }
        }
        // mtx.unlock(); 
        this_thread::sleep_for(chrono::milliseconds(2));
    }
}

void sellTicket3(int index)
{
    while (tickCount > 0)
    {

        // mtx.lock();
        {
        // lock_guard函数删除了拷贝构造函数和操作符=重载，类似于scoped_ptr，但保留了构造函数
        // lock_guard<mutex> lock(mtx); // 在这个局部作用域中，程序结束后自动析构，如果中间return了也会析构
        unique_lock<mutex> temp_lock(mtx); // 类似于unique_ptr，虽然删除了拷贝构造函数和操作符=重载，但是扩展了右值引用
        if (tickCount > 0)
        {
                cout << "窗口:" << index << "卖出第" << tickCount << "张票。" << endl;
                tickCount--;
        }
        }
        // mtx.unlock();
        this_thread::sleep_for(chrono::milliseconds(2));
    }
}

/*
多进程程序：
竞态条件：多线程程序执行的结果是一致的，不会随着CPU对线程不同的调用顺序，而产生不同的运行结果。
*/
int main()
{
    list<thread> tlist;
    int thread_num = 3;
    for (int i = 0; i < thread_num;i++)
    {
        tlist.push_back(thread(sellTicket3, i));
    }
    for (thread &t : tlist)
    {
        t.join();
    }

        return 0;
}